package com.ppch.wuwamanus.controller;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ppch.wuwamanus.agent.GameAssistantManus;
import com.ppch.wuwamanus.annotation.AuthCheck;
import com.ppch.wuwamanus.common.BaseResponse;
import com.ppch.wuwamanus.common.ResultUtils;
import com.ppch.wuwamanus.config.CosClientConfig;
import com.ppch.wuwamanus.config.FileUtils;
import com.ppch.wuwamanus.config.ImageUrlUtils;
import com.ppch.wuwamanus.exception.ErrorCode;
import com.ppch.wuwamanus.exception.ThrowUtils;
import com.ppch.wuwamanus.mapper.ChatHistoryMapper;
import com.ppch.wuwamanus.model.constant.UserConstant;
import com.ppch.wuwamanus.model.dto.chat_history.ChatHistoryQueryRequest;
import com.ppch.wuwamanus.model.entity.ChatHistory;
import com.ppch.wuwamanus.model.entity.User;
import com.ppch.wuwamanus.model.enums.ChatHistoryMessageTypeEnum;
import com.ppch.wuwamanus.service.ChatHistoryService;
import com.ppch.wuwamanus.service.UserService;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.client.advisor.api.Advisor;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.tool.ToolCallback;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.http.codec.ServerSentEvent;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import reactor.core.publisher.Flux;

import java.io.IOException;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * Package:ppch-ai-agent
 * ClassName:AiApplicationController
 *
 * @Author 泡泡茶壶
 * @Create 2025/8/2 13:19
 * @Version 1.0
 * Description:
 * AI自主规划智能体接口开发
 * 1.同步接口：等后端处理完成才返回数据
 * 2.SSE接口：实时流式输出（打字机效果）
 */
@RestController
@RequestMapping("/ai/manus")
@Slf4j
public class ManusController {

    @Resource
    private ToolCallback[] allTools;

    @Resource
    private ToolCallbackProvider toolCallbackProvider;

    @Resource
    private ChatModel dashscopeChatModel;

    @Resource
    private FileUtils fileUtils;

    @Resource
    private ChatHistoryService chatHistoryService;

    @Resource
    private ChatHistoryMapper chatHistoryMapper;

    @Resource
    private CosClientConfig cosClientConfig;

    @Resource
    private Advisor dashScopeRetrievalAugmentationAdvisor;

    /**
     * 去除字符串中的重复内容
     * @param str 字符串文本
     * @return 返回去重后的内容
     */
    public String removeRepetitiveContent(String str){
        // 使用 StrUtil.split 按逗号分割字符串，得到 List
        List<String> urlList = StrUtil.split(str, '。');
        // 使用 CollUtil.distinct 去除重复项
        List<String> uniqueUrlList = CollUtil.distinct(urlList);
        // 使用 StrUtil.join 将列表重新连接为字符串
        return StrUtil.join("。", uniqueUrlList);
    }

    /**
     *  AI 对话接口，支持多轮对话和对话记忆（直接返回 Flux 对象）
     */
    @GetMapping(value = "/chat/flux/sse/history")
    public Flux<ServerSentEvent<String>> doChatWithHistoryByFlux(Long chatId, String userMessage, String messageType, Long userId) {
        ThrowUtils.throwIf(StrUtil.isBlank(userMessage),ErrorCode.PARAMS_ERROR,"用户提示词不能为空");
        ThrowUtils.throwIf(StrUtil.isBlank(messageType),ErrorCode.PARAMS_ERROR,"提示词类型不能为空");
        ThrowUtils.throwIf(ObjectUtil.isEmpty(userId),ErrorCode.PARAMS_ERROR,"用户ID不能为空");
        //用于判断是否新增会话，便于前端刷新会话列表
        boolean addNewConversation = false;
        //如果对话 ID 为空，使用雪花算法生成唯一ID
        if(ObjectUtil.isEmpty(chatId)){
            chatId = IdUtil.getSnowflake().nextId();
            addNewConversation = true;
        }
        //1.保存用户消息到对话历史中
        chatHistoryService.addChatHistory(chatId, userMessage, messageType, userId);
        GameAssistantManus gameAssistantManus = new GameAssistantManus(allTools, toolCallbackProvider, dashscopeChatModel,
                fileUtils, chatId, userId,20, chatHistoryMapper, cosClientConfig, dashScopeRetrievalAugmentationAdvisor,
                chatHistoryService);
        Flux<String> responseStream = gameAssistantManus.runReactiveStream(userMessage);

        Long finalChatId = chatId;
        boolean finalAddNewConversation = addNewConversation;
        //StringBuffer线程安全
        StringBuffer aiResponseBuffer = new StringBuffer();

        // 主响应流
        Flux<ServerSentEvent<String>> mainFlux = responseStream
                .map(chunk -> {
                    // 处理自定义事件：PDF文件事件
                    if (chunk.contains("SEND_PDF_FILE")) {
                        String pdfUrl = chunk.replace("SEND_PDF_FILE", "");
                        // 检查数据库是否已存在该记录，避免重复
                        LambdaQueryWrapper<ChatHistory> queryWrapper = chatHistoryService.getQueryWrapper(
                                new ChatHistoryQueryRequest(null, pdfUrl, ChatHistoryMessageTypeEnum.SEND_PDF_FILE.getValue(),
                                        finalChatId, userId, null));
                        List<ChatHistory> list = chatHistoryService.list(queryWrapper);
                        if (list.size() > 0) {
                            return ServerSentEvent.<String>builder()
                                    .data("已生成相同的PDF文件，可查看历史记录。")
                                    .build();
                        } else {
                            // 保存PDF事件到数据库
                            chatHistoryService.addChatHistory(finalChatId, pdfUrl,
                                    ChatHistoryMessageTypeEnum.SEND_PDF_FILE.getValue(), userId);
                            return ServerSentEvent.<String>builder()
                                    .event(ChatHistoryMessageTypeEnum.SEND_PDF_FILE.getValue())
                                    .data(pdfUrl)
                                    .build();
                        }
                    }
                    // 处理自定义事件：抽卡分析事件
                    else if (chunk.contains("SEND_CARD_DRAWING_ANALYSIS")) {
                        String analysisData = chunk.replace("SEND_CARD_DRAWING_ANALYSIS", "");
                        LambdaQueryWrapper<ChatHistory> queryWrapper = chatHistoryService.getQueryWrapper(
                                new ChatHistoryQueryRequest(null, analysisData, ChatHistoryMessageTypeEnum.SEND_CARD_DRAWING_ANALYSIS.getValue(),
                                        finalChatId, userId, null));
                        List<ChatHistory> list = chatHistoryService.list(queryWrapper);
                        if (list.size() > 0) {
                            return ServerSentEvent.<String>builder()
                                    .data("无需重复查询，可查看历史记录。")
                                    .build();
                        } else {
                            chatHistoryService.addChatHistory(finalChatId, analysisData,
                                    ChatHistoryMessageTypeEnum.SEND_CARD_DRAWING_ANALYSIS.getValue(), userId);
                            return ServerSentEvent.<String>builder()
                                    .event(ChatHistoryMessageTypeEnum.SEND_CARD_DRAWING_ANALYSIS.getValue())
                                    .data(analysisData)
                                    .build();
                        }
                    }
                    // 普通消息事件
                    else {
                        aiResponseBuffer.append(chunk); // 收集AI响应
                        return ServerSentEvent.<String>builder()
                                .data(chunk)
                                .build();
                    }
                })
                .doOnComplete(() -> {
                    // 流完成后保存AI消息
                    String uniqueResult = removeRepetitiveContent(aiResponseBuffer.toString());
                    chatHistoryService.addChatHistory(finalChatId, uniqueResult,
                            ChatHistoryMessageTypeEnum.AI.getValue(), userId);
                })
                .doOnError(error -> {
                    // 错误处理
                    log.error("AI响应流处理失败, chatId: {}", finalChatId, error);
                })
                .onErrorResume(e -> {
                    String errorMessage = "AI 回复失败：" + e.getMessage();
                    aiResponseBuffer.append(errorMessage);
                    return Flux.just(ServerSentEvent.<String>builder()
                            .data(errorMessage)
                            .build());
                });
        // finalAddNewConversation 为 true 时才新增会话事件
        if (finalAddNewConversation) {
            ServerSentEvent<String> newConversationEvent = ServerSentEvent.<String>builder()
                    .event(ChatHistoryMessageTypeEnum.SEND_ADD_NEW_CONVERSATION.getValue())
                    .data(finalChatId.toString())
                    .build();
            return mainFlux.concatWithValues(newConversationEvent);
        } else {
            return mainFlux;
        }
    }

    /**
     * 调用 SSE 对话接口，实现 AI 对话（对话历史和多轮对话能力）
     * 1.对话历史功能：将用户输入以及收集 AI 流式响应结果，并将消息保存到数据库中，实现对话历史功能。
     * 2.多轮对话功能：在 ToolCallAgent 类的 reason（）方法中根据对话 ID 查询对应的历史记录，并解析（user/ai消息）
     *   将记录添加到内部维护的 List<Message> 中，在每次对话时都将之前的消息作为上下文发送给 AI 实现多轮对话记忆。
     * 2.由于前端 EventSource 不支持携带 Cookie，所以需要前端传 userId。
     */
    @GetMapping(value = "/chat/sse/history")
    public SseEmitter doChatWithHistory(Long chatId, String userMessage, String messageType, Long userId) {
        //基础参数校验
        ThrowUtils.throwIf(StrUtil.isBlank(userMessage), ErrorCode.PARAMS_ERROR, "用户输入不能为空");
        ThrowUtils.throwIf(StrUtil.isBlank(messageType), ErrorCode.PARAMS_ERROR, "消息类型不能为空");
        ThrowUtils.throwIf(ObjectUtil.isEmpty(userId), ErrorCode.PARAMS_ERROR, "用户 ID 不能为空");
        //用于判断是否新增会话，便于前端刷新会话列表
        boolean addNewConversation = false;
        //如果对话 ID 为空，使用雪花算法生成唯一ID
        if(ObjectUtil.isEmpty(chatId)){
            chatId = IdUtil.getSnowflake().nextId();
            addNewConversation = true;
        }
        //1.保存用户消息到对话历史中
        chatHistoryService.addChatHistory(chatId, userMessage, messageType, userId);
        // TODO：多轮对话基本实现（只存储在数据库中）
        GameAssistantManus gameAssistantManus = new GameAssistantManus(allTools, toolCallbackProvider, dashscopeChatModel,
                fileUtils,chatId,userId,20,chatHistoryMapper,cosClientConfig, dashScopeRetrievalAugmentationAdvisor,chatHistoryService);
        //2.调用 AI 获取响应数据
        Flux<String> responseStream = gameAssistantManus.runReactiveStream(userMessage);
        //3.创建SseEmitter(30分钟超时)
        SseEmitter emitter = new SseEmitter(1800000L);
        //4.用于收集AI响应内容
        StringBuilder aiResponseBuilder = new StringBuilder();
        Long finalChatId = chatId;
        boolean finalAddNewConversation = addNewConversation;
        //5.订阅响应式流
        responseStream.subscribe(
                chunk -> {
                    try {
                        //TODO：监听工具调用结果，如果生成了 PDF 文件，发送 SEND_PDF_FILE 事件，携带 PDF 文件的链接地址
                        if (chunk.contains("SEND_PDF_FILE")) {
                            //1.去掉响应结果中的 "SEND_PDF_FILE" 前缀
                            String pdfUrl = chunk.replace("SEND_PDF_FILE", "");
                            //2.获取事件类型名称
                            String message_type_send_pdf_file = ChatHistoryMessageTypeEnum.SEND_PDF_FILE.getValue();
                            //3.插入PDF事件记录到数据库（TODO：避免发送重复事件和插入重复数据）
                            //3.1.查询数据库中是否已经存在该 PDF 记录
                            LambdaQueryWrapper queryWrapper = chatHistoryService.getQueryWrapper(
                                    new ChatHistoryQueryRequest(null, pdfUrl,message_type_send_pdf_file,
                                            finalChatId, userId, null));
                            List list = chatHistoryService.list(queryWrapper);
                            //3.2.如果存在，则不发送事件且不插入数据库
                            if(list.size() > 0){
                                chunk = "已生成相同的PDF文件，可查看历史记录。";
                            }else {
                                //3.3.如果不存在，则发送自定义事件
                                SseEmitter.SseEventBuilder event = SseEmitter.event()
                                        .name(message_type_send_pdf_file)
                                        .data(pdfUrl);
                                emitter.send(event);
                                //3.4.插入PDF事件记录到数据库
                                chatHistoryService.addChatHistory(finalChatId, pdfUrl,
                                        message_type_send_pdf_file, userId);
                                //4.修改响应给前端的文案
                                chunk = "。PDF访问链接："+pdfUrl+"。";
                            }
                        }
                        //TODO：监听工具调用结果，发送 SEND_CARD_DRAWING_ANALYSIS 事件，携带抽卡分析结果数据
                        if (chunk.contains("SEND_CARD_DRAWING_ANALYSIS")) {
                            //1.去掉响应结果中的 "SEND_CARD_DRAWING_ANALYSIS" 前缀
                            String analysisData = chunk.replace("SEND_CARD_DRAWING_ANALYSIS", "");
                            //2.获取事件类型名称
                            String message_type_send_card_drawing_analysis = ChatHistoryMessageTypeEnum.SEND_CARD_DRAWING_ANALYSIS.getValue();
                            //3.插入抽卡分析事件记录到数据库（TODO：避免发送重复事件和插入重复数据）
                            //3.1.查询数据库中是否已经存在该 SEND_CARD_DRAWING_ANALYSIS 记录
                            LambdaQueryWrapper queryWrapper = chatHistoryService.getQueryWrapper(
                                    new ChatHistoryQueryRequest(null, analysisData,message_type_send_card_drawing_analysis,
                                            finalChatId, userId, null));
                            List list = chatHistoryService.list(queryWrapper);
                            //3.2.如果存在，则不发送事件且不插入数据库
                            if(list.size() > 0){
                                chunk = "无需重复查询，可查看历史记录。";
                            }else {
                                //3.3.如果不存在，则发送自定义事件
                                SseEmitter.SseEventBuilder event = SseEmitter.event()
                                        .name(message_type_send_card_drawing_analysis)
                                        .data(analysisData);
                                emitter.send(event);
                                //3.4.插入抽卡分析事件记录到数据库
                                chatHistoryService.addChatHistory(finalChatId, analysisData,
                                        message_type_send_card_drawing_analysis, userId);
                            }
                        }
                        //TODO：发送 ADD_NEW_CONVERSATION 事件，通知前端新增了会话并携带新增会话的chatId
                        if(finalAddNewConversation){
                            SseEmitter.SseEventBuilder event = SseEmitter.event()
                                    .name(ChatHistoryMessageTypeEnum.SEND_ADD_NEW_CONVERSATION.getValue())
                                    .data(finalChatId);
                            emitter.send(event);
                        }
                        //2.收集 AI 响应内容
                        aiResponseBuilder.append(chunk);
                        //3.发送响应数据
                        emitter.send(chunk);
                    } catch (IOException e) {
                        log.error("发送SSE消息失败", e);
                        emitter.completeWithError(e);
                    }
                },
                error -> {
                    // 错误处理
                    log.error("AI响应流处理失败, chatId: {}", finalChatId, error);
                    emitter.completeWithError(error);
                    //4.流式返回出错时，也要保存 AI 消息到对话历史记录
                    String errorMessage = "AI 回复失败：" + error.getMessage();
                    chatHistoryService.addChatHistory(finalChatId, errorMessage,
                            ChatHistoryMessageTypeEnum.AI.getValue(), userId);
                },
                () -> {
                    // 完成处理
                    emitter.complete();
                    // 去除字符串中的重复内容
                    String uniqueResult = this.removeRepetitiveContent(aiResponseBuilder.toString());
                    //5.流式返回完成后，保存 AI 消息到对话历史记录
                    chatHistoryService.addChatHistory(finalChatId, uniqueResult,
                            ChatHistoryMessageTypeEnum.AI.getValue(), userId);
                }
        );
        //6.返回 SSE 流式响应
        return emitter;
    }

}
